package com.reebake.ideal.document.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.reebake.ideal.document.entity.*;
import com.reebake.ideal.document.service.*;
import com.reebake.ideal.document.util.RenderUtil;
import com.reebake.ideal.document.util.DocumentConvertUtil;
import com.reebake.ideal.infra.client.ContentClobClient;
import com.reebake.ideal.infra.entity.ContentClobDTO;
import com.reebake.ideal.json.util.JsonUtil;
import com.reebake.ideal.storage.client.FileDownloadClient;
import com.reebake.ideal.storage.client.FileUploadClient;
import com.reebake.ideal.storage.entity.FileInfoDTO;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class DocumentRenderServiceImpl implements DocumentRenderService {
    @Autowired
    private DocumentTemplateService documentTemplateService;
    @Autowired
    private DocumentTemplateFileService documentTemplateFileService;
    @Autowired
    private FileUploadClient fileUploadClient;
    @Autowired
    private FileDownloadClient fileDownloadClient;
    @Autowired
    private DocumentMakeService documentMakeService;
    @Autowired
    private DocumentMakeFileService documentMakeFileService;

    private List<DocumentTemplateFile> queryFiles(String documentTemplateId){
        LambdaQueryWrapper<DocumentTemplateFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DocumentTemplateFile::getDocumentTemplateId, documentTemplateId);

        return documentTemplateFileService.list(queryWrapper);
    }

    @Override
    @SneakyThrows
    public DocumentRenderResult render(String documentTemplateId, Map<String, Object> data) {
        DocumentRenderResult documentRenderResult = new DocumentRenderResult();
        documentRenderResult.setDocumentTemplateId(documentTemplateId);

        List<DocumentTemplateFile> documentTemplateFiles = queryFiles(documentTemplateId);

        List<DocumentRenderFile> list = new ArrayList<>();
        for(DocumentTemplateFile documentTemplateFile : documentTemplateFiles){
            String filename = RenderUtil.renderText(documentTemplateFile.getFilename(), data);
            String originalFilename = documentTemplateFile.getOriginalFilename();
            String fromExtName = FileNameUtil.extName(originalFilename);
            String toExtName = FileNameUtil.extName(filename);

            Resource resource = fileDownloadClient.downloadById(documentTemplateFile.getFileDetailId()).getBody();
            assert resource != null;
            InputStream is = resource.getInputStream();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            if(fromExtName.equalsIgnoreCase("XLS")
                    || fromExtName.equalsIgnoreCase("XLSX")){
                RenderUtil.renderExcel(is, data, bos);
            }else if(fromExtName.equalsIgnoreCase("DOC")
                    || fromExtName.equalsIgnoreCase("DOCX")) {
                RenderUtil.renderWord(is, data, bos);
            }else{
                String sourceText = IoUtil.readUtf8(is);
                String renderedText = RenderUtil.renderText(sourceText, data);
                bos.writeBytes(renderedText.getBytes(CharsetUtil.CHARSET_UTF_8));
            }
            DocumentRenderFile documentRenderFile = new DocumentRenderFile();
            bos.flush();
            documentRenderFile.setInputStream(new ByteArrayInputStream(bos.toByteArray()));
            bos.close();
            documentRenderFile.setFilename(filename);
            documentRenderFile.setDocumentTemplateFileId(documentTemplateFile.getDocumentTemplateFileId());
            documentRenderFile.setFileDetailId(documentTemplateFile.getFileDetailId());
            list.add(convert(fromExtName, toExtName, documentRenderFile));
        }
        documentRenderResult.getRenderedItems().addAll(list);

        return documentRenderResult;
    }

    @SneakyThrows
    private DocumentRenderFile convert(String fromExt, String toExt, DocumentRenderFile documentRenderFile){
        if(ObjUtil.isNull(documentRenderFile)
        || fromExt.equalsIgnoreCase(toExt)){
            return documentRenderFile;
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DocumentConvertUtil.convert(documentRenderFile.getInputStream(), fromExt, bos, toExt);
        bos.flush();
        documentRenderFile.getInputStream().close();
        documentRenderFile.setInputStream(new ByteArrayInputStream(bos.toByteArray()));
        bos.close();
        documentRenderFile.setFilename(FileNameUtil.mainName(documentRenderFile.getFilename()) + StrPool.DOT + toExt);
        return documentRenderFile;
    }

    @Transactional
    @Override
    public void make(String documentTemplateId, String bizCode, Map<String, Object> data) {
        DocumentRenderResult documentRenderResult = render(documentTemplateId, data);
        DocumentTemplate documentTemplate = documentTemplateService.getById(documentTemplateId);
        String code = RenderUtil.renderText(documentTemplate.getCode(), data);

        DocumentMake documentMake = new DocumentMake();
        documentMake.setDocumentTemplateId(documentTemplateId);
        documentMake.setCode(code);
        documentMake.setAccessRole(documentTemplate.getAccessRole());
        documentMake.setBizCode(bizCode);
        documentMake.setBizType(documentTemplate.getBizType());
        documentMake.setEnableAccessLog(documentTemplate.getEnableAccessLog());
        documentMake.setSecretDegree(documentTemplate.getSecretDegree());
        documentMake.setTitle(documentTemplate.getTitle());

        documentMakeService.save(documentMake);

        // save files
        for(DocumentRenderFile documentRenderFile : documentRenderResult.getRenderedItems()) {
            DocumentMakeFile documentMakeFile = new DocumentMakeFile();
            documentMakeFile.setDocumentMakeId(documentMake.getDocumentMakeId());
            documentMakeFile.setFilename(documentRenderFile.getFilename());
            documentMakeFile.setDocumentTemplateFileId(documentRenderFile.getDocumentTemplateFileId());
            FileInfoDTO fileInfoDTO = fileUploadClient.upload(documentRenderFile.getFilename(), IoUtil.readBytes(documentRenderFile.getInputStream()));
            documentMakeFile.setFileDetailId(fileInfoDTO.getId());

            documentMakeFileService.save(documentMakeFile);
        }
    }
}
